﻿namespace Text2Rss.Web.Services
{
	#region Using directives
	using System;
	using System.IO;
	using System.Text;
	#endregion

	/// <summary>
	/// The class parses text paragraph by paragraph
	/// </summary>
	public class TextParser : ITextParser
	{
		#region Fields
		private Stream file;
		private Encoding encoding;
		#endregion

		#region Constructor
		/// <summary>
		/// .ctor
		/// </summary>
		/// <param name="file"></param>
		public TextParser(Stream file)
		{
			Init(file, Encoding.UTF8);
		}

		/// <summary>
		/// .ctor
		/// </summary>
		/// <param name="file"></param>
		/// <param name="encoding"></param>
		public TextParser(Stream file, Encoding encoding)
		{
			Init(file, encoding);
		}

		public void Init(Stream stream, Encoding encoding)
		{
			if (stream == null)
			{
				throw new ArgumentNullException("file");
			}

			if (encoding == null)
			{
				throw new ArgumentNullException("encoding");
			}

			this.file = stream;
			this.encoding = encoding;

			CurrentPosition = 0;
		}
		#endregion

		#region Properties
		public string Text { get; set; }

		public long CurrentPosition { get; set; }
		#endregion

		#region Methods
		/// <summary>
		/// Moves pointer to position
		/// </summary>
		/// <param name="position">Position to move</param>
		/// <returns>true in case of success</returns>
		public virtual bool GoTo(long position)
		{
			if (file.Length < position)
			{
				return false;
			}

			this.CurrentPosition = position;

			return true;
		}

		/// <summary>
		/// Reads text til eof
		/// </summary>
		/// <param name="size">Minimum size of text</param>
		/// <returns>True if at least one symbol was loaded</returns>
		public virtual bool ReadText(int size)
		{
			if (size <= 0)
			{
				throw new ArgumentException("size");
			}

			file.Position = CurrentPosition;

			using (var reader = new StreamReader(file, encoding))
			{
				return ReadTextFromCurrentPosition(reader, size);
			}
		}

		private bool ReadTextFromCurrentPosition(StreamReader reader, int size)
		{
			StringBuilder builder = new StringBuilder();

			do
			{
				ReadMainBlock(reader, builder, size);

				ReadTilEos(reader, builder);

				Text = builder.ToString();
			} while (
				!string.IsNullOrEmpty(Text) &&
				string.IsNullOrWhiteSpace(Text));

			Text = Text.Trim();

			return !string.IsNullOrEmpty(Text);
		}

		#region Helpers
		private void ReadMainBlock(StreamReader reader, StringBuilder builder, int size)
		{
			if (!reader.EndOfStream)
			{
				char[] buffer = new char[size];
				int readCount = reader.ReadBlock(buffer, 0, size);
				builder.Append(buffer, 0, readCount);

				CurrentPosition += reader.CurrentEncoding
					.GetByteCount(buffer, 0, readCount);
			}
		}

		private void ReadTilEos(StreamReader reader, StringBuilder builder)
		{
			bool eol = false;
			char[] buffer = new char[1];

			while (!reader.EndOfStream && !eol)
			{
				int readCount = reader.ReadBlock(buffer, 0, 1);
				if (readCount > 0)
				{
					eol = buffer[0] == '\n';
					builder.Append(buffer[0]);

					CurrentPosition += reader.CurrentEncoding
						.GetByteCount(buffer);
				}
			}
		}
		#endregion
		#endregion
	}
}